package com.zycx.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.zycx.common.constant.JWT.JWTConstants;
import com.zycx.common.entity.EntityUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Description:  登陆是生成 token
 * Created by zycx on 2018-12-04 22:59
 * Created with IntelliJ IDEA.
 */
@Slf4j
public class JjwtCreateToken {

    // ====== JWT 获取token ===================================
    public static String getToken(Map params) {
        log.info("获取 token ...");
        String token = "";
        try {
            token = JWT.create()
                    .withAudience(params.get("id").toString())                                               // 将 user id 保存到 token 里面
                    .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7))           //  过期时间  一周
                    .sign(Algorithm.HMAC256(params.get("passWord").toString()));                             // 以 password 作为 token 的密钥
        } catch (Exception ignore) {
        }
        log.info("token = "+ token);
        return token;
    }


    //  ======== JJWT 获取token =============================================
    public String createJJWTToken(Object id,String subject) throws Exception {
        //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        Map<String,Object> claims = new HashMap<String,Object>();
        claims.put("uid", id);
        claims.put("token_type", JWTConstants.Token_Request_Header);

        // 生成签名的时候使用的秘钥secret,这个方法本地封装了的，一般可以从本地配置文件中读取，切记这个秘钥不能外露哦。
        // 它就是你服务端的私钥，在任何场景都不应该流露出去。
        // 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        SecretKey key = generalKey();

        //下面就是在为payload添加各种标准声明和私有声明了
        JwtBuilder builder = Jwts.builder()               //这里其实就是new一个JwtBuilder，设置jwt的body
                .setClaims(claims)                        //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setId(EntityUtils.getId())               //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(new Date(System.currentTimeMillis()))   //iat: jwt的签发时间
                .setSubject(subject)                      //sub(Subject)：代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .signWith(signatureAlgorithm, key)        //设置签名使用的签名算法和签名使用的秘钥
                .setExpiration(JWTConstants.Token_Expired_Jwt);      //设置过期时间
        return builder.compact();
    }

    /**
     * 解密jwt
     * @param jwt
     * @return Claims
     * @throws Exception
     */
    public Claims parseJWT(String jwt) throws Exception{
        SecretKey key = generalKey();               // 签名秘钥，和生成的签名的秘钥一模一样
        Claims claims = Jwts.parser()               // 得到DefaultJwtParser
                .setSigningKey(key)                 // 设置签名的秘钥
                .parseClaimsJws(jwt).getBody();     // 设置需要解析的jwt
        return claims;
    }

    /**
     * 由字符串生成加密key
     * @return
     */
    public SecretKey generalKey(){
        byte[] encodedKey = Base64.decodeBase64(JWTConstants.Token_JWT_SECRET);
        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
    }

    public static void main(String[] args) throws Exception {
        JjwtCreateToken util =  new JjwtCreateToken();
        String token =util.createJJWTToken("fabdbsajdb","{id:100,name:xiaohong}");
        System.out.println("token = "+token);
        Claims c = util.parseJWT(token);
        System.out.println(c.getId());
        System.out.println(c.getIssuedAt());
        System.out.println(c.getSubject());
        System.out.println(c.getIssuer());
        System.out.println(c.get("uid", String.class));
    }


}
